﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Hallenberg.SmartClient.Events;
using Hallenberg.SmartClient.Interfaces;
using Hallenberg.SmartClient.Menu;
using Hallenberg.SmartClient.Services;
using Hallenberg.SmartClient.IO;

namespace Hallenberg.SmartClient
{
    [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "UnityContainer should not be disposed like this!")]
    public sealed class WorkItemController
    {
        private List<Assembly> m_RegisteredAssemblies;
        private bool m_TypeHandlersRegistered;

        public IServiceLocator Services { get; private set; }

        public Modules Modules
        {
            get
            {
                return this.Services.Resolve<Modules>();
            }
        }

        public IMenuManager MenuManagers
        {
            get
            {
                return this.Services.Resolve<IMenuManager>();
            }
        }

        public IEventManager Events
        {
            get
            {
                return this.Services.Resolve<IEventManager>();
            }
        }

        public IModuleLoader ModuleLoader
        {
            get
            {
                return Services.Resolve<IModuleLoader>();
            }
        }

        public WorkItemController()
        {
            m_RegisteredAssemblies = new List<Assembly>();

            Services = new ServiceLocator();

            Services.RegisterInstance<IServiceLocator>(Services);
            Services.RegisterInstance<WorkItemController>(this);

            Services.RegisterInstance<IMenuManager>(new MenuManager());
            Services.RegisterInstance<IResourceMapper>(new ResourceMapper());

            Services.RegisterInstance<IEntityTranslatorService>(new EntityTranslatorService());
            Services.RegisterType<Modules>();

            Services.RegisterInstance<IEventManager>(new EventManager(Services));

            Services.RegisterType<IModuleLoader, ModuleLoader>();
        }

        public void Initialize()
        {
            foreach (IBaseModule module in Modules.AllModules)
            {
                module.Initialize();
            }
        }

        internal void AddRegisteredAssembly(Assembly assembly)
        {
            Debug.Assert(null != assembly, "null != assembly");
            if (null == assembly)
            {
                throw new ArgumentNullException("assembly");
            }

            m_RegisteredAssemblies.Add(assembly);
        }

        internal IEnumerable<Assembly> GetRegisteredAssemblies()
        {
            return m_RegisteredAssemblies;
        }

        public void HandleType(Type assemblyType)
        {
            ///////////////////////////////////////////////////////////////////
            // Register type handlers.
            ///////////////////////////////////////////////////////////////////

            RegisterTypeHandlers();

            ///////////////////////////////////////////////////////////////////
            // Handled provided type.
            ///////////////////////////////////////////////////////////////////

            IClassTypeHandler typeHandler = Services.ResolveAll<IClassTypeHandler>().FirstOrDefault(th => th.CanHandle(assemblyType));

            if (typeHandler != null)
            {
                typeHandler.Handle(assemblyType);

                foreach (IClassTypeHandlerExtender extender in Services.ResolveAll<IClassTypeHandlerExtender>().Where(thex => thex.CanHandle(assemblyType)))
                {
                    extender.Handle(assemblyType);
                }
            }

            ///////////////////////////////////////////////////////////////
            // Check if assemblyType is a class and then examine is it has 
            // methods or properties decorated with attributes that should 
            // be handled.
            ///////////////////////////////////////////////////////////////

            foreach (MethodInfo foundMethod in assemblyType.GetMethods())
            {
                List<Attribute> foundAttributes = new List<Attribute>();

                foreach (Attribute attrib in foundMethod.GetCustomAttributes(true))
                {
                    foundAttributes.Add(attrib);
                }

                foreach (Attribute foundAttribute in foundAttributes)
                {
                    IAttributeTypeHandler attributeTypeHandler = this.Services.ResolveAll<IAttributeTypeHandler>().FirstOrDefault(ath => ath.HandledType == foundAttribute.GetType());

                    if (attributeTypeHandler != null)
                    {
                        attributeTypeHandler.Handle(assemblyType, foundMethod, foundAttribute, foundAttributes.ToArray());

                        foreach (IAttributeTypeHandlerExtender extender in Services.ResolveAll<IAttributeTypeHandlerExtender>().Where(thex => thex.HandledType == foundAttribute.GetType()))
                        {
                            extender.Handle(assemblyType, foundMethod, foundAttribute, foundAttributes.ToArray());
                        }
                    }
                }
            }

        }

        private void RegisterTypeHandlers()
        {
            if (!m_TypeHandlersRegistered)
            {
                foreach (Assembly assembly in GetRegisteredAssemblies())
                {
                    Type[] types =
                    { 
                        typeof(IClassTypeHandler), 
                        typeof(IClassTypeHandlerExtender),
                        typeof(IAttributeTypeHandler), 
                        typeof(IAttributeTypeHandlerExtender)                        
                    };

                    foreach (Type type in types)
                    {
                        foreach (Type foundTypeHandler in assembly.GetTypes().Where(typ => typ.GetInterfaces().Contains(type)))
                        {
                            if (foundTypeHandler.IsAbstract == false)
                            {
                                Services.RegisterType(type, foundTypeHandler, foundTypeHandler.Name);
                            }
                        }
                    }
                }

                m_TypeHandlersRegistered = true;
            }
        }
    }
}
